package cndw.core;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cndw.core.cfg.ServerConfig;
import cndw.core.dispatch.ActionManager;
import cndw.core.dispatch.ChannelSessionManager;
import cndw.core.dispatch.CoreDispatcher;
import cndw.core.dispatch.Dispatcher;
import cndw.core.dispatch.SessionManager;
import cndw.core.listener.ServerCloseListener;
import cndw.core.util.LogConst;
import cndw.core.util.annotation.ThreadSafe;
import cndw.core.util.resource.ResourceMonitor;

/**
 * @author zuojie.x
 */
@ThreadSafe
public class GameServer {
	private static final Logger log = LoggerFactory.getLogger(LogConst.CORE);

	private ApplicationContext context;
	// 业务派遣
	private Dispatcher dispatcher;
	// Action管理器
	private ActionManager actionManager;
	// session管理
	private SessionManager sessionManager;
	
	private static GameServer gameServer;
	
	/**
	 * 启动服务
	 */
	public static void start() {
		if (gameServer == null) {
			synchronized (GameServer.class) {
				if (gameServer == null)
					gameServer = new GameServer();
			}
		}
		try {
			gameServer.doStart();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 关闭服务
	 */
	public static void stop() {
		synchronized (GameServer.class) {
			if (gameServer != null) {
				gameServer.doStop();
			} else {
				log.error("server not running.");
			}
		}
	}

	private GameServer() {
		context = new ClassPathXmlApplicationContext(ServerConfig.stringValue("DefaultApplicationContext"));
		actionManager = new ActionManager(context);
		dispatcher = new CoreDispatcher(actionManager);
		sessionManager = new ChannelSessionManager();
		context.getBean(ResourceMonitor.class).monitoring();
	}

	private void doStart() throws Exception {
		// tcp
		int tcpPort = ServerConfig.intValue("game.tcp.port");
		final ChannelHandler channelHandler = new GameServerHandler(dispatcher, sessionManager);
		ServerBootstrap tcpBootstrap = new ServerBootstrap();
		try {
			tcpBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup());
			tcpBootstrap.channel(NioServerSocketChannel.class);
			tcpBootstrap.childHandler(new ChannelInitializer<Channel>() {
				@Override
				protected void initChannel(Channel channel) throws Exception {
					channel.pipeline().addLast(channelHandler);
				}
			});
			tcpBootstrap.childOption(ChannelOption.TCP_NODELAY, ServerConfig.booleanValue("TCP_NODELAY"));
			tcpBootstrap.childOption(ChannelOption.SO_KEEPALIVE, ServerConfig.booleanValue("SO_KEEPALIVE"));
			tcpBootstrap.childOption(ChannelOption.SO_REUSEADDR, ServerConfig.booleanValue("SO_REUSEADDR"));
			ChannelFuture future = tcpBootstrap.bind(tcpPort);
			Runtime.getRuntime().addShutdownHook(new ShutdownHook());
			log.info("server started.");
			future.channel().closeFuture().sync();
		} finally {
			tcpBootstrap.shutdown();
		}
	}

	/**
	 * 停止服务器
	 */
	private void doStop() {
		dispatcher.stop();
		Map<String, ServerCloseListener> listeners = context
				.getBeansOfType(ServerCloseListener.class);
		for (ServerCloseListener listener : listeners.values()) {
			listener.close();
		}
		log.info("server stopped .");
	}

	private class ShutdownHook extends Thread {
		@Override
		public void run() {
			GameServer.stop();
		}
	}
}
